package mge.particles 
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.DisplayObjectContainer;
    import flash.display.Shape;
    import flash.display.SpreadMethod;
    import flash.display.Sprite;
    import flash.geom.ColorTransform;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.geom.Vector3D;
    import mge.Timer;
    
	/**
   * ...
   * @author ivan
   */
  public class ParticleSystem extends Sprite
  {
    
    public function ParticleSystem(bitmap:Bitmap) 
    {
      this.bitmap = bitmap;
      
      timer = new Timer(1000);
      timer.start();
      systemTmr = new Timer(1000);
      bitmapIndex = 0;
    }
    
    public function update(timeScale:Number = 1):void
    {
      if (!started) return;
      
      var delta:Number = timer.getDelta() * timeScale;
      
      if (!systemTmr.finished())
      {        
        var shouldHaveEmittedNum:int = 1 + (_descriptor.systemLifeTime * systemTmr.getDelta()) * _descriptor.emission;
        while (emmited < shouldHaveEmittedNum)
        {
          addParticle();
        }
      }
      else 
      {
          if (particles.length == 0) started = false;
          if (_descriptor.continuous && !stopRequested) fire();
      }
      
      graphics.clear();
      
      for (var i:int = 0; i < particles.length; i++)
      {
        var p:ParticleSprite = particles[i];
        p.update(delta);
        if (p.currentLife >= p.lifeTime)
        {
            p.clear();
            particles.splice(i--, 1);
            pool.push(p);
        }
      }
        
      timer.start();
    }
    
    private var stopRequested:Boolean;
    public function stop():void
    {
      stopRequested = true;
    }
    
    public function fire():void
    {        
        timer.start();
        systemTmr = new Timer(_descriptor.systemLifeTime*1000);
        systemTmr.start();
        started = true;
        emmited = 0;
        stopRequested = false;
    }
    
    private function addParticle():void
    {
      var p:ParticleSprite;
      
      if (pool.length == 0)
        p = new ParticleSprite(tile, this);
      else
      {
          p = pool.pop();
          p.revive(tile);
      }
        
      // Position
      p.position = _descriptor.position.clone();
      
      // Gravity
      p.gravity = _descriptor.gravityNormDir.clone();
      p.gravity.scaleBy(random(_descriptor.gravityMin, _descriptor.gravityMax));
      
      // Note:
      // Velocity = (rv * spread/360) + vn * (360-spread)/360
      // Where rv=random vector, vn=velocity normalized (both normalized).
      
      // Velocity
      var vn:Vector3D = _descriptor.velocityNormDir.clone();
      vn.normalize();
      vn.scaleBy( (360 - _descriptor.spread) / 360);
      
      // Velocity is affected by spread
      var rv:Vector3D = new Vector3D(random( -1, 1), random( -1, 1), random( -1, 1));
      rv.normalize();
      rv.scaleBy(_descriptor.spread/360);
      
      var v:Vector3D = new Vector3D;
      v = v.add(vn);
      v = v.add(rv);
      
      v.scaleBy(random(_descriptor.velocityMin, _descriptor.velocityMax));
      p.velocity = v;
      
      p.radialAcceleration = random(_descriptor.radialAccelerationMin, _descriptor.radialAccelerationMax);
      
      p.lifeTime = random(_descriptor.particleLifeTimeMin, _descriptor.particleLifeTimeMax);
      
      if (_descriptor.scaleStart > _descriptor.scaleEnd)
      {
        var maxVar:Number = (_descriptor.scaleStart - _descriptor.scaleEnd) * _descriptor.scaleVar * Math.random();
        p.scaleStart = _descriptor.scaleStart - maxVar;
        p.scaleEnd = _descriptor.scaleEnd + maxVar;
      }
      else
      {
        maxVar = (_descriptor.scaleEnd - _descriptor.scaleStart) * _descriptor.scaleVar * Math.random();
        p.scaleStart = _descriptor.scaleStart + maxVar;
        p.scaleEnd = _descriptor.scaleEnd - maxVar;
      }
            
      p.colorStart = _descriptor.colorStart;
      p.colorEnd = _descriptor.colorEnd;
      
      p.spinStart = _descriptor.spinStart + (Math.random() * _descriptor.spinVar - _descriptor.spinVar * 0.5);
      p.spinEnd = _descriptor.spinEnd + (Math.random() * _descriptor.spinVar - _descriptor.spinVar * 0.5);
      
      particles.push(p);
      
      emmited++;
    }
    
    private function random(min:Number, max:Number):Number
    {
        return (min + ((max - min) * Math.random()));
    }
    
    
    private function set bitmapIndex(val:int):void
    {
        _bitmapIndex = val;
        var w:int = bitmap.width / 4;
        var h:int = bitmap.height / 4;
        var x:int = ((int)(_bitmapIndex / 4)) * w;
        var y:int = ((int)(_bitmapIndex % 4)) * h;
        
        var bmpData:BitmapData = new BitmapData(w, h);
        
        bmpData.copyPixels(bitmap.bitmapData, new Rectangle(x, y, w, h), new Point(0, 0));
        tile = new Bitmap(bmpData, "auto", true);
    }
    private var _bitmapIndex:int;
    
    public function get particleNum():int { return particles.length; }
    
    private var particles:Vector.<ParticleSprite> = new Vector.<ParticleSprite>();
    private var pool:Vector.<ParticleSprite> = new Vector.<ParticleSprite>();
    private var timer:Timer;
    private var systemTmr:Timer;
    private var bitmap:Bitmap;
    private var tile:Bitmap;
    private var emmited:int;
    private var started:Boolean;
    
    public var _descriptor:Descriptor;
    
    // Returns true when the effect has finished or is not started yet.
    public function get finished():Boolean { return started == false; }
    
    public function get descriptor():Descriptor { return _descriptor; }
    public function set descriptor(val:Descriptor):void
    {
      _descriptor = val;
      bitmapIndex = _descriptor.bitmapIndex;
    }
  }

}